home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 5817 / 5817.xpi / chrome / resource / sqlite.js < prev    next >
Text File  |  2010-02-11  |  37KB  |  1,201 lines

  1. //TODO: some documentation for this file
  2. //TODO: some simplification
  3. //as of now, depends on mFuncConfirm (a function), mBlobPrefs (from prefs) & setStrForNull (in sqlitefn); but the latter 2 have default values and even if the first one is not set, there will be no confirmation before executing. So, this file is pretty independent now.
  4.  
  5. let EXPORTED_SYMBOLS = ["SQLiteTypes", "SQLiteHandler", "SQLiteFn"];
  6.  
  7. //https://developer.mozilla.org/en/mozIStorageValueArray
  8. const SQLiteTypes = {
  9.   NULL   : 0,
  10.   INTEGER: 1,
  11.   FLOAT  : 2,
  12.   TEXT   : 3,
  13.   BLOB   : 4
  14. };
  15.  
  16. const Cc = Components.classes;
  17. const Ci = Components.interfaces;
  18. const Cr = Components.results;
  19. const Cu = Components.utils;
  20.  
  21. var stmtCallback = {
  22.   handleResult: function(aResultSet) {
  23.     for (let row = aResultSet.getNextRow(); row; row = aResultSet.getNextRow()) {
  24.  
  25.       this.logMessage("handleResult\n" + 11);
  26. //       let value = row.getResultByName("column_name");
  27.     }
  28.   },
  29.  
  30.   handleError: function(aError) {
  31.     alert("Error in executeAsync: " + aError.message);
  32.   },
  33.  
  34.   handleCompletion: function(aReason) {
  35.     if (aReason != Ci.mozIStorageStatementCallback.REASON_FINISHED) {
  36.       alert("Query canceled or aborted!");
  37.       return false;
  38.     }
  39.     else
  40.       return true;
  41.   }
  42. };
  43.  
  44. function SQLiteHandler() {
  45.   this.storageService = Cc["@mozilla.org/storage/service;1"].getService(Ci.mozIStorageService);
  46.   this.consoleService = Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService);
  47.   this.promptService = Cc["@mozilla.org/embedcomp/prompt-service;1"].getService(Ci.nsIPromptService);
  48. }
  49.  
  50. SQLiteHandler.prototype = {
  51.   dbConn: null,
  52.   mbShared: true,
  53.   mOpenStatus: "",
  54.  
  55.   aTableData: null,       // Stores 2D array of table data
  56.   aTableType: null,
  57.   aColumns: null,
  58.  
  59.   colNameArray: null,
  60.   resultsArray: null,
  61.   statsArray: null,
  62.  
  63.   maDbList: ["main", "temp"],
  64.   mLogicalDbName: "main", //for main, temp and attached databases
  65.  
  66.   lastErrorString: "",
  67.   miTime: 0, //time elapsed during queries
  68.  
  69.   mFuncConfirm: null,
  70.   mBlobPrefs: {sStrForBlob: 'BLOB', bShowSize: true, iMaxSizeToShowData: 100, iHowToShowData: 0},
  71.  
  72.   //array to hold names of added functions; will be used in removing functions
  73.   maAddedFunctions: [],
  74.  
  75.   // openDatabase: opens a connection to the db file nsIFile
  76.   // bShared = true: first attempt shared mode, then unshared
  77.   // bShared = false: attempt unshared cache mode only
  78.   openDatabase: function(nsIFile, bShared) {
  79.     this.closeConnection();
  80.  
  81.     try {
  82.       if (!bShared) // dummy exception to reach catch to use openUnsharedDatabase
  83.         throw 0;
  84.  
  85.       this.dbConn = this.storageService.openDatabase(nsIFile);
  86.       this.mbShared = true;
  87.       // if the db does not exist it does not give us any indication
  88.       // this.dbConn.lastErrorString returns "not an error"
  89.     }
  90.     catch (e) { //attempt unshared connection
  91.       try {
  92.         this.dbConn = this.storageService.openUnsharedDatabase(nsIFile);
  93.         this.mbShared = false;
  94.         // if the db does not exist it does not give us any indication
  95.         // this.dbConn.lastErrorString returns "not an error"
  96.       }
  97.       catch (e) {
  98.         try {
  99.           this.dbConn = this.openSpecialProfileDatabase(nsIFile);
  100.         }
  101.         catch (e) {
  102.           this.onSqlError(e, "Error in opening file " + nsIFile.leafName + " - perhaps this is not an sqlite db file", null);
  103.           return false;
  104.         }
  105.       }
  106.     }
  107.     
  108.     if(this.dbConn == null)
  109.       return false;
  110.     this.mOpenStatus = this.mbShared?"Shared":"Exclusive";
  111.     return true;
  112.   },
  113.  
  114.   openSpecialProfileDatabase: function(nsIFile) {
  115.     var DirectoryService = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
  116.     var profD = DirectoryService.get('ProfD', Ci.nsIFile);
  117.     if (nsIFile.parent.equals(profD)) {
  118.       switch (nsIFile.leafName.toLowerCase()) {
  119.         case "places.sqlite":
  120.           if ('nsPIPlacesDatabase' in Ci) {
  121.             return Cc["@mozilla.org/browser/nav-history-service;1"].getService(Ci.nsINavHistoryService).QueryInterface(Ci.nsPIPlacesDatabase).DBConnection;
  122.           }
  123.           break;
  124.       }
  125.     }
  126.     return null;
  127.   },
  128.  
  129.   openSpecialDatabase: function(sSpecialName) {
  130.     if (sSpecialName != "memory")
  131.       return false;
  132.     this.closeConnection();
  133.  
  134.     try {
  135.       this.dbConn = this.storageService.openSpecialDatabase(sSpecialName);
  136.     }
  137.     catch (e) {
  138.       this.onSqlError(e, "Error in opening in memory database", null);
  139.       return false;
  140.     }
  141.  
  142.     if(this.dbConn == null)
  143.       return false;
  144.     this.mOpenStatus = "Memory";
  145.     return true;
  146.   },
  147.  
  148.   closeConnection: function() {
  149.     if (this.dbConn != null) {
  150.       try {
  151.         this.dbConn.close();
  152.       } catch (e) {
  153.         this.dbConn = null;
  154.       }
  155.     }
  156.  
  157.     this.aTableData = null;
  158.     this.aTableType = null;
  159.     this.aColumns = null;
  160.     this.mOpenStatus = "";
  161.     this.maAddedFunctions = [];
  162.   },
  163.  
  164.   createFunction: function(fnName, argLength, fnObject) {
  165.     if (funcNamesAll.indexOf(fnName) != -1) {
  166.       this.logMessage("Cannot create function called: " + fnName + "\nThis name belongs to a core, aggregate or datetime function.");
  167.       return;
  168.     }
  169.     try {
  170.       this.dbConn.createFunction(fnName, argLength, fnObject);
  171.     } catch (e) {
  172.       this.onSqlError(e, "Failed to create storage function: " + fnName);
  173.       return false;
  174.     }
  175.     this.maAddedFunctions.push(fnName);
  176.     return true;
  177.   },
  178.  
  179.   createAggregateFunction: function(fnName, argLength, fnObject) {
  180.     if (funcNamesAll.indexOf(fnName) != -1) {
  181.       this.logMessage("Cannot create aggregate function called: " + fnName + "\nThis name belongs to a core, aggregate or datetime function.");
  182.       return;
  183.     }
  184.     try {
  185.       this.dbConn.createAggregateFunction(fnName, argLength, fnObject);
  186.     } catch (e) {
  187.       this.onSqlError(e, "Failed to create storage function: " + fnName);
  188.       return false;
  189.     }
  190.     this.maAddedFunctions.push(fnName);
  191.     return true;
  192.   },
  193.  
  194.   //remove all functions created by createFunction & createAggregateFunction
  195.   removeAllFunctions: function() {
  196.     var i = 0;
  197.     while (i < this.maAddedFunctions.length) {
  198.       try {
  199.         var step = 0;
  200.         var fnName = this.maAddedFunctions[i];
  201.         step = 1;
  202.         this.dbConn.removeFunction(fnName);
  203.         step = 2;
  204.         this.maAddedFunctions.splice(i, 1);
  205.       } catch (e) {
  206.         i++;
  207.         this.onSqlError(e, "removeAllFunctions: Failed while attempting to remove storage function: " + fnName + '\nstep: ' + step);
  208.       }
  209.     }
  210.   },
  211.  
  212.   getOpenStatus: function() { return this.mOpenStatus; },
  213.   getElapsedTime: function() { return this.miTime; },
  214.   getRecords: function() { return this.aTableData; },
  215.   getRecordTypes: function() { return this.aTableType; },
  216.   getColumns: function() { return this.aColumns; },
  217.   getLastError: function() { return this.lastErrorString; },
  218.  
  219.   setErrorString: function() {
  220.     this.lastErrorString = this.dbConn.lastErrorString;
  221.   },
  222.  
  223.   get logicalDbName() { return this.mLogicalDbName; },
  224.   get schemaVersion() { return this.dbConn.schemaVersion; },
  225.  
  226.   setLogicalDbName: function(sDbName) {
  227.     this.mLogicalDbName = sDbName;
  228.   },
  229.  
  230.   setBlobPrefs: function(objBlobPrefs) {
  231.     this.mBlobPrefs = objBlobPrefs;
  232.   },
  233.  
  234.   setFuncConfirm: function(oFunc) {
  235.     this.mFuncConfirm = oFunc;
  236.   },
  237.  
  238.   getPrefixedName: function(objName, sDbName) {
  239.     if (sDbName == "")
  240.       sDbName = this.mLogicalDbName;
  241.  
  242.     return SQLiteFn.quoteIdentifier(sDbName) + "." + SQLiteFn.quoteIdentifier(objName);
  243.   },
  244.  
  245.   getPrefixedMasterName: function(sDbName) {
  246.     if (sDbName == "")
  247.       sDbName = this.mLogicalDbName;
  248.  
  249.     if (sDbName == "temp")
  250.       return "sqlite_temp_master";
  251.     else
  252.       return SQLiteFn.quoteIdentifier(sDbName) + ".sqlite_master";
  253.   },
  254.  
  255.   getFileName: function() {
  256.     if (this.dbConn != null)
  257.       return this.dbConn.databaseFile.leafName;
  258.     return null;
  259.   },
  260.  
  261.   getFile: function() {
  262.     if (this.dbConn != null)
  263.       return this.dbConn.databaseFile;
  264.     return null;
  265.   },
  266.  
  267.   get sqliteVersion() {
  268.     this.selectQuery("SELECT sqlite_version()");
  269.     return this.aTableData[0][0];
  270.   },
  271.  
  272.   setSetting: function(sSetting, sValue) {
  273.     if (sSetting == "encoding" || sSetting == "temp_store_directory")
  274.       sValue = "'" + sValue + "'";
  275.     var sQuery = "PRAGMA " + sSetting + " = " + sValue;
  276.     //do not execute in a transaction; some settings will cause error
  277.     this.selectQuery(sQuery);
  278.  
  279.     return this.getSetting(sSetting);
  280.   },
  281.  
  282.   getSetting: function(sSetting) {
  283.     var iValue = null;
  284.     try {
  285.       this.selectQuery("PRAGMA " + sSetting);
  286.       iValue = this.aTableData[0][0];
  287.       return iValue;
  288.     } catch (e) {
  289.       if (sSetting == "temp_store_directory")
  290.         return '';
  291.       else
  292.         this.alert("PRAGMA " + sSetting + ": exception - " + e.message);
  293.     }
  294.   },
  295.   
  296.   tableExists: function(sTable, sDbName) {
  297.     if (typeof sDbName == "undefined")
  298.       return this.dbConn.tableExists(sTable);
  299.     else {
  300.       var aList = this.getObjectList("table", sDbName);
  301.       if (aList.indexOf(sTable) >= 0)
  302.         return true;
  303.     }
  304.     return false;
  305.   },
  306.  
  307.   //getObjectList: must return an array of names of type=argument 
  308.   // Type = master|table|index|view|trigger,
  309.   //empty array if no object found
  310.   getObjectList: function(sType, sDb) {
  311.     if (sDb == "")
  312.       sDb = this.mLogicalDbName;
  313.  
  314.     var aResult = [];
  315.  
  316.     if (sType == "master") {
  317.       aResult = ["sqlite_master"];
  318.       if (sDb == "temp")
  319.         aResult = ["sqlite_temp_master"];
  320.       return aResult;    
  321.     }
  322.  
  323.     var sTable = this.getPrefixedMasterName(sDb);
  324.     var sQuery = "SELECT name FROM " + sTable + " WHERE type = '"
  325.           + sType + "' ORDER BY name";
  326.     this.selectQuery(sQuery);
  327.  
  328.     for (var i = 0; i < this.aTableData.length; i++)
  329.       aResult.push(this.aTableData[i][0]);
  330.  
  331.     return aResult;
  332.   },
  333.   // loadTableData: retrieves data from a table including rowid if needed
  334.   // return r: -1 = error, 0 = ok without extracol,
  335.   // r > 0 means column number of extracol starting with 1
  336.   loadTableData: function(sObjType, sObjName, aArgs) {
  337.     if (sObjType != "master" && sObjType != "table" && sObjType != "view")
  338.       return -1;
  339.       
  340.     var sCondition = aArgs.sWhere?aArgs.sWhere:"";
  341.     var iLimit = aArgs.iLimit?aArgs.iLimit:-1;
  342.     var iOffset = aArgs.iOffset?aArgs.iOffset:0;
  343.     var sOrder = "";
  344.     if (aArgs.aOrder && aArgs.aOrder.length > 0) {
  345.       var aTemp = [];
  346.       for (var i = 0; i < aArgs.aOrder.length; i++) {
  347.         aTemp.push(SQLiteFn.quoteIdentifier(aArgs.aOrder[i][0]) + " " + aArgs.aOrder[i][1]);
  348.       }
  349.       sOrder = " ORDER BY " + aTemp.join(", ") + " ";
  350.     }
  351.  
  352.     var extracol = "";
  353.     var iRetVal = 0;
  354.     var sLimitClause = " LIMIT " + iLimit + " OFFSET " + iOffset;
  355.     
  356.     if (sObjType == "table" || sObjType == "master") {
  357.       //find whether the rowid is needed 
  358.       //or the table has an integer primary key
  359.       var rowidcol = this.getTableRowidCol(sObjName);
  360.       if (rowidcol["name"] == "rowid") {
  361.         extracol = " `rowid`, ";
  362.         iRetVal = 1;
  363.       }
  364.     }
  365.     //table having columns called rowid behave erratically
  366.     sObjName = this.getPrefixedName(sObjName, "");
  367.     this.selectQuery("SELECT " + extracol + " * FROM " + sObjName + " " + sCondition + sOrder + sLimitClause);
  368.     return iRetVal;
  369.   },
  370.  
  371.   //for tables and views
  372.   getRowCount: function(sObjName, sCondition) {
  373.     var iValue = 0;
  374.     sObjName = this.getPrefixedName(sObjName, "");
  375.     var sQuery = "SELECT count(*) FROM " + sObjName + " " + sCondition;
  376.     this.selectQuery(sQuery);
  377.  
  378.     iValue = this.aTableData[0][0];
  379.     return iValue;
  380.   },
  381.  
  382.   //for count of indexes/triggers of a table
  383.   getObjectCount: function(sTable, sDb) {
  384.     var sMaster = this.getPrefixedMasterName(sDb);
  385.     var sQuery = "SELECT type, count(*) AS cnt FROM " + sMaster + " WHERE tbl_name = '" + sTable + "' AND type IN ('index', 'trigger') GROUP BY type";
  386.  
  387.     var stmt = this.dbConn.createStatement(sQuery);
  388.     var oRow = {indexCount: 0, triggerCount: 0};
  389.     try {
  390.       while (stmt.executeStep()) {
  391.         if (stmt.row.type == 'index')
  392.           oRow.indexCount = stmt.row.cnt;
  393.         if (stmt.row.type == 'trigger')
  394.           oRow.triggerCount = stmt.row.cnt;
  395.       }
  396.     } finally {
  397.       stmt.reset();
  398.     }
  399.     return oRow;
  400.   },
  401.   
  402.   emptyTable: function(sTableName) {
  403.     var sQuery = "DELETE FROM " + this.getPrefixedName(sTableName, "");
  404.     return this.confirmAndExecute([sQuery], "Delete All Records");
  405.   },
  406.  
  407.   renameTable: function(sOldName, sNewName, sDb) {
  408.     var sQuery = "ALTER TABLE " + this.getPrefixedName(sOldName, sDb) + " RENAME TO " + SQLiteFn.quoteIdentifier(sNewName);
  409.     return this.confirmAndExecute([sQuery], "Rename table " + sOldName);
  410.   },
  411.  
  412.   analyzeTable: function(sTableName) {
  413.     var sQuery = "ANALYZE " + this.getPrefixedName(sTableName, "");
  414.     return this.confirmAndExecute([sQuery], "Analyze Table");
  415.   },
  416.  
  417.   //sObject = TABLE/INDEX/COLLATION;
  418.   reindexObject: function(sObjectType, sObjectName) {
  419.     var sQuery = "REINDEX " + this.getPrefixedName(sObjectName, "");
  420.     return this.confirmAndExecute([sQuery], sQuery);
  421.   },
  422.  
  423.   //sObjType = TABLE/INDEX/VIEW/TRIGGER;
  424.   dropObject: function(sObjType, sObjectName) {
  425.     var sQuery = "DROP " + sObjType + " " + this.getPrefixedName(sObjectName, "");
  426.     return this.confirmAndExecute([sQuery], sQuery);
  427.   },
  428.  
  429.   addColumn: function(sTable, aColumn) {
  430.     var aQueries = [];
  431.     var coldef = SQLiteFn.quoteIdentifier(aColumn["name"]) + " " + aColumn["type"];
  432.     if (aColumn["notnull"])
  433.       coldef += " NOT NULL ";
  434.     if (aColumn["dflt_value"] != null) {
  435.       coldef += " DEFAULT " + aColumn["dflt_value"];
  436.     }
  437.     var sTab = this.getPrefixedName(sTable, "");
  438.     var sQuery = "ALTER TABLE " + sTab + " ADD COLUMN " + coldef;
  439.     return this.confirmAndExecute([sQuery], "Add Column to Table " + sTable);
  440.   },
  441.  
  442.   // selectQuery : execute a select query and store the results
  443.   selectQuery: function(sQuery, bBlobAsHex) {
  444.     this.aTableData = new Array();
  445.     this.aTableType = new Array();
  446. // if aColumns is not null, there is a problem in tree display
  447.     this.aColumns = null;        
  448.     var bResult = false;
  449.  
  450.     var timeStart = Date.now();
  451.     try { // mozIStorageStatement
  452.       var stmt = this.dbConn.createStatement(sQuery);
  453.     }
  454.     catch (e) {
  455.       // statement will be undefined because it throws error);
  456.       this.onSqlError(e, "Likely SQL syntax error: " + sQuery, this.dbConn.lastErrorString);
  457.       this.setErrorString();
  458.       return false;
  459.     }
  460.     
  461.     var iCols = 0;
  462.     var iType, colName;
  463.     try {
  464.       // do not use stmt.columnCount in the for loop, fetches the value again and again
  465.       iCols = stmt.columnCount;
  466.       this.aColumns = new Array();
  467.       var aTemp, aType;
  468.       for (var i = 0; i < iCols; i++) {
  469.         colName = stmt.getColumnName(i);
  470.         aTemp = [colName, iType];
  471.         this.aColumns.push(aTemp);  
  472.       }
  473.     } catch (e) { 
  474.       this.onSqlError(e, "Error while fetching column name: " + colName, null);
  475.       this.setErrorString();
  476.       return false;
  477.     }
  478.  
  479.     var cell;
  480.     var bFirstRow = true;
  481.     try {
  482.       while (stmt.executeStep()) {
  483.         aTemp = [];
  484.         aType = [];
  485.         for (i = 0; i < iCols; i++) {
  486.           iType = stmt.getTypeOfIndex(i);
  487.           if (bFirstRow) {
  488.             this.aColumns[i][1] = iType;
  489.           }
  490.           switch (iType) {
  491.             case stmt.VALUE_TYPE_NULL: 
  492.               cell = null;//SQLiteFn.getStrForNull();
  493.               break;
  494.             case stmt.VALUE_TYPE_INTEGER:
  495.               cell = stmt.getInt64(i);
  496.               break;
  497.             case stmt.VALUE_TYPE_FLOAT:
  498.               cell = stmt.getDouble(i);
  499.               break;
  500.             case stmt.VALUE_TYPE_TEXT:
  501.               cell = stmt.getString(i);
  502.               break;
  503.             case stmt.VALUE_TYPE_BLOB: //TODO: handle blob properly
  504.               if (bBlobAsHex) {
  505.                   var iDataSize = {value:0};
  506.                   var aData = {value:null};
  507.                   stmt.getBlob(i, iDataSize, aData);
  508.                   cell = SQLiteFn.blob2hex(aData.value);
  509.               }
  510.               else {
  511.                 cell = this.mBlobPrefs.sStrForBlob;
  512.                 if (this.mBlobPrefs.bShowSize) {
  513.                   var iDataSize = {value:0};
  514.                   var aData = {value:null};
  515.                   stmt.getBlob(i, iDataSize, aData);
  516.                   cell += " (Size: " + iDataSize.value + ")";
  517.                   if (iDataSize.value <= this.mBlobPrefs.iMaxSizeToShowData || this.mBlobPrefs.iMaxSizeToShowData < 0) {
  518.                     if (this.mBlobPrefs.iHowToShowData == 1)
  519.                       cell = this.convertBlobToStr(aData.value);
  520.                     if (this.mBlobPrefs.iHowToShowData == 0)
  521.                       cell = SQLiteFn.blob2hex(aData.value);
  522.                   }
  523.                 }
  524.               }
  525.               break;
  526.             default: sData = "<unknown>"; 
  527.           }
  528.           aTemp.push(cell);
  529.           aType.push(iType);
  530.         }
  531.         this.aTableData.push(aTemp);
  532.         this.aTableType.push(aType);
  533.         bFirstRow = false;
  534.       }
  535.       this.miTime = Date.now() - timeStart;
  536.     } catch (e) { 
  537.       this.onSqlError(e, "Query: " + sQuery + " - executeStep failed", null);
  538.       this.setErrorString();
  539.       return false;
  540.     } finally {
  541.       stmt.reset();
  542.     }
  543.     this.setErrorString();
  544.     return true;
  545.   },
  546.  
  547.   exportTable: function(sTableName, sDbName, oFormat) {
  548.     var sQuery = "SELECT * FROM " + this.getPrefixedName(sTableName, sDbName);
  549.     this.selectQuery(sQuery, true);
  550.     var arrData = this.getRecords();
  551.     var arrColumns = this.getColumns();
  552.     var arrTypes = this.getRecordTypes();
  553.  
  554.     if (oFormat.name == "csv")
  555.       return getCsvFromArray(arrData, arrTypes, arrColumns, oFormat);  
  556.   },
  557.  
  558.   convertBlobToStr: function(aData) {
  559.     var str = '';
  560.     for (var i = 0; i < aData.length; i++) {
  561.       str += String.fromCharCode(aData[i]);
  562.     }
  563.     return str;
  564.   },
  565.  
  566.   convertBlobToHex: function(aData) {
  567.     var hex_tab = '0123456789ABCDEF';
  568.     var str = '';
  569.     for (var i = 0; i < aData.length; i++) {
  570.       str += hex_tab.charAt(aData[i] >> 4 & 0xF) + hex_tab.charAt(aData[i] & 0xF);
  571.     }
  572.     return "X'" + str + "'";
  573.   },
  574.   // selectBlob : execute a select query to return blob
  575.   selectBlob: function(sTable, sField, sWhere) {
  576.     var sQuery = ["SELECT", SQLiteFn.quoteIdentifier(sField), "FROM", this.getPrefixedName(sTable, ""), "WHERE", sWhere].join(' ');
  577.     try { // mozIStorageStatement
  578.       var stmt = this.dbConn.createStatement(sQuery);
  579.     }
  580.     catch (e) {
  581.       // statement will be undefined because it throws error);
  582.       this.onSqlError(e, "Likely SQL syntax error: " + sQuery, this.dbConn.lastErrorString);
  583.       this.setErrorString();
  584.       return false;
  585.     }
  586.     
  587.     if (stmt.columnCount != 1)
  588.       return false;
  589.  
  590.     var cell;
  591.     try {
  592.       stmt.executeStep();
  593.       if (stmt.getTypeOfIndex(0) != stmt.VALUE_TYPE_BLOB)
  594.         return false;
  595.  
  596.       var iDataSize = {value:0};
  597.       var aData = {value:null};
  598.       stmt.getBlob(0, iDataSize, aData);
  599.       cell = "BLOB (Size: " + iDataSize.value + ")";
  600.       //return [iDataSize.value, aData.value];
  601.       return aData.value;
  602.     } catch (e) { 
  603.       this.onSqlError(e, "Query: " + sQuery + " - executeStep failed", null);
  604.       this.setErrorString();
  605.       return false;
  606.     } finally {
  607.       stmt.reset();
  608.     }
  609.     this.setErrorString();
  610.     return true;
  611.   },
  612.  
  613.   // getTableRowidCol : execute a pragma query and return the results
  614.   getTableRowidCol: function(sTableName) {
  615.     var aCols = this.getTableInfo(sTableName, "");
  616.     var aReturn = [];
  617.  
  618.     var iNumPk = 0, iIntPk = 0;
  619.     for(var i = 0; i < aCols.length; i++) {
  620.       var row = this.aTableData[i];
  621.       var type = aCols[i].type;
  622.       var pk = aCols[i].pk;
  623.       type = type.toUpperCase();
  624.       if(pk == 1) {
  625.         iNumPk++;
  626.         if (type == "INTEGER") {
  627.           iIntPk++;
  628.           aReturn["name"] = aCols[i].name;
  629.           aReturn["cid"] = aCols[i].cid;
  630.         }
  631.       }
  632.     }
  633.     if (iNumPk == 1 && iIntPk == 1)
  634.       return aReturn;
  635.     
  636.     aReturn["name"] = "rowid";
  637.     aReturn["cid"] = 0;
  638.     return aReturn;
  639.   },
  640.  
  641.   getPragmaSchemaQuery: function(sPragma, sObject, sDbName) {
  642.     if (sDbName == "")
  643.       sDbName = this.mLogicalDbName;
  644.     return "PRAGMA " + SQLiteFn.quoteIdentifier(sDbName) + "." + sPragma + "(" + SQLiteFn.quoteIdentifier(sObject) + ")";
  645.   },
  646.  
  647.   getIndexDetails: function(sIndexName, sDb) {
  648.     var aReturn = {tbl_name: '', unique: 0};
  649.  
  650.     var row = this.getMasterInfo(sIndexName, '');
  651.     aReturn.tbl_name = row.tbl_name;
  652.  
  653.     //to find whether duplicates allowed
  654.     var aList = this.getIndexList(aReturn.tbl_name, "");
  655.     for(var i = 0; i < aList.length; i++) {
  656.       if(aList[i].name == sIndexName)
  657.         aReturn.unique = aList[i].unique;
  658.     }
  659.     
  660.     return aReturn;
  661.   },
  662.     
  663.   select : function(file,sql,param) {
  664.     var ourTransaction = false;
  665.     if (this.dbConn.transactionInProgress) {
  666.       ourTransaction = true;
  667.       this.dbConn.beginTransactionAs(this.dbConn.TRANSACTION_DEFERRED);
  668.     }
  669.     var statement = this.dbConn.createStatement(sql);
  670.     if (param) {
  671.       for (var m = 2, arg = null; arg = arguments[m]; m++) 
  672.         statement.bindUTF8StringParameter(m-2, arg);
  673.     }
  674.     try {
  675.       var dataset = [];
  676.       while (statement.executeStep()) {
  677.         var row = [];
  678.         for (var i = 0, k = statement.columnCount; i < k; i++)
  679.           row[statement.getColumnName(i)] = statement.getUTF8String(i);
  680.  
  681.         dataset.push(row);
  682.       }
  683.       // return dataset;
  684.     }
  685.     finally {
  686.       statement.reset();
  687.     }
  688.     if (ourTransaction) {
  689.       this.dbConn.commitTransaction();
  690.     }
  691.         return dataset;
  692.   },
  693.   
  694.   executeAsync: function(aQueries) {
  695.     var timeStart = Date.now();
  696.  
  697.     var stmt, aStmt = [];
  698.     for(var i = 0; i < aQueries.length; i++) {
  699.       try {
  700.         stmt = this.dbConn.createStatement(aQueries[i]);
  701.         aStmt.push(stmt);
  702.         //stmt.executeAsync(stmtCallback);
  703.       }
  704.       catch (e) {
  705.         this.setErrorString();
  706.         this.onSqlError(e, "Error in createStatement: " + aQueries[i], this.dbConn.lastErrorString);
  707.         return false;
  708.       }
  709.     }
  710.  
  711.     var stmtPending = this.dbConn.executeAsync(aStmt, aStmt.length, stmtCallback);
  712.     this.setErrorString();
  713.  
  714.     this.miTime = Date.now() - timeStart;
  715.     return true;
  716.   },  
  717.  
  718.   executeTransaction: function(aQueries) {
  719.     //IS THIS NEEDED?
  720.     //commit, if some leftover transaction is in progress
  721.     if (this.dbConn.transactionInProgress)
  722.       this.dbConn.commitTransaction();
  723.  
  724.     var timeStart = Date.now();
  725.     //begin a transaction, iff no transaction in progress
  726.     if (!this.dbConn.transactionInProgress)
  727.       this.dbConn.beginTransaction();
  728.  
  729.     for(var i = 0; i < aQueries.length; i++) {
  730.       try {
  731.         var statement = this.dbConn.createStatement(aQueries[i]);
  732.         statement.execute();
  733.       }
  734.       catch (e) {
  735.         this.setErrorString();
  736.         // statement will be undefined because it throws error);
  737.         this.onSqlError(e, "Likely SQL syntax error: " + aQueries[i], this.dbConn.lastErrorString);
  738.         this.setErrorString();
  739.         if (this.dbConn.transactionInProgress) {
  740.           this.dbConn.rollbackTransaction();
  741.         }
  742.         return false;
  743.       }
  744.       finally {
  745.         statement.reset();
  746.       }
  747.     }
  748.     //commit transaction, if reached here
  749.     if (this.dbConn.transactionInProgress)
  750.       this.dbConn.commitTransaction();
  751.  
  752.     this.miTime = Date.now() - timeStart;
  753.     return true;
  754.   },  
  755.  
  756.  // executeWithParams : execute a query with parameter binding
  757.   executeWithParams: function(sQuery, aParamData) {
  758.     try {
  759.       var stmt = this.dbConn.createStatement(sQuery);
  760.     } catch (e) {
  761.       this.onSqlError(e, "Create statement failed: " + sQuery, this.dbConn.lastErrorString);
  762.       this.setErrorString();
  763.       return false;
  764.     }
  765.  
  766.     for (var i = 0; i < aParamData.length; i++) {
  767.       var aData = aParamData[i];
  768.       switch (aData[2]) {
  769.         case "blob":
  770.           try {
  771.             stmt.bindBlobParameter(aData[0], aData[1], aData[1].length);
  772.           } catch (e) {
  773.             this.onSqlError(e, "Binding failed for parameter: " + aData[0], this.dbConn.lastErrorString);
  774.             this.setErrorString();
  775.             return false;
  776.           }
  777.           break;
  778.       }
  779.     }
  780.     try {
  781.       stmt.execute();
  782.     } catch (e) {
  783.       this.onSqlError(e, "Execute failed: " + sQuery, this.dbConn.lastErrorString);
  784.       this.setErrorString();
  785.       return false;
  786.     }
  787.  
  788.     try {
  789.       stmt.reset();
  790.       stmt.finalize();
  791.     } catch (e) {
  792.         this.onSqlError(e, "Failed to reset/finalize", this.dbConn.lastErrorString);
  793.         this.setErrorString();
  794.         return false;
  795.     }
  796.     return true;
  797.   },
  798.  
  799.   confirmAndExecute: function(aQueries, sMessage, confirmPrefName, aParamData) {
  800.     var answer = true;
  801.     //function for confirmation should not be hardcoded
  802.     if (this.mFuncConfirm != null)
  803.       answer = (this.mFuncConfirm)(aQueries, sMessage, confirmPrefName);
  804.  
  805.     if(answer) {
  806.       if (aParamData)
  807.         return this.executeWithParams(aQueries[0], aParamData);
  808.       else
  809.         return this.executeTransaction(aQueries);
  810.     }
  811.     return false;
  812.   },
  813.  
  814.   executeWithoutConfirm: function(aQueries, aParamData) {
  815.     if (aParamData)
  816.       return this.executeWithParams(aQueries[0], aParamData);
  817.     else
  818.       return this.executeTransaction(aQueries);
  819.   },
  820.  
  821.   executeSimpleSQLs: function(aQueries) {
  822.     for (var i=0; i < aQueries.length; i++) {
  823.       this.dbConn.executeSimpleSQL(aQueries[i]);    
  824.     }
  825.   },
  826.  
  827.   attachDatabase: function(sName, sPath) {
  828.     if (sName == 'main' || sName == 'temp')
  829.       return false;
  830.  
  831.     var sQuery = "ATTACH DATABASE " + SQLiteFn.quote(sPath) + " AS " + SQLiteFn.quoteIdentifier(sName);
  832.     return this.selectQuery(sQuery);  
  833.   },
  834.  
  835.   onSqlError: function(ex, msg, SQLmsg) {
  836.     msg = "SQLiteManager: " + msg;
  837.     if (SQLmsg != null)
  838.       msg += " [ " + SQLmsg + " ]";
  839.  
  840.     msg += "\n";
  841.     msg += "Exception Name: " + ex.name + "\n" +
  842.           "Exception Message: " + ex.message;
  843.     this.alert(msg);
  844.     Cu.reportError(msg);
  845.     return true;
  846.   },
  847.  
  848.   alert: function(sMsg) {
  849.     this.promptService.alert(null, "SQLite Manager Alert", sMsg);
  850.   },
  851.  
  852.   logMessage: function(sMsg) {
  853.     this.consoleService.logStringMessage("SQLiteManager: " + sMsg);
  854.   },
  855.  
  856.   getMasterInfo: function(sObjName, sDbName) {
  857.     var sTable = this.getPrefixedMasterName(sDbName);
  858.     var sQuery = "SELECT * FROM " + sTable + " WHERE name = '" + sObjName + "'";
  859.     var stmt = this.dbConn.createStatement(sQuery);
  860.     var aRows = [];
  861.     try {
  862.       while (stmt.executeStep()) {
  863.         var oRow = {};
  864.  
  865.         oRow.type = stmt.row.type;
  866.         oRow.name = stmt.row.name;
  867.         oRow.tbl_name = stmt.row.tbl_name;
  868.         oRow.rootpage = stmt.row.rootpage;
  869.         oRow.sql = stmt.row.sql;
  870.  
  871.         aRows.push(oRow);
  872.       }
  873.     } finally {
  874.       stmt.reset();
  875.     }
  876.     if (aRows.length > 0)
  877.       return aRows[0];
  878.     else
  879.       return aRows;
  880.   },
  881.  
  882. /////////////////////////////////////////////
  883. //The following functions are for Pragmas to query the database schema
  884. /////////////////////////////////////////////
  885.  
  886. //function for attached db list (not main & temp)
  887. //returns all columns
  888.   getAttachedDbList: function() {
  889.     var sQuery = "PRAGMA database_list";
  890.     var stmt = this.dbConn.createStatement(sQuery);
  891.     var aRows = [];
  892.     try {
  893.       while (stmt.executeStep()) {
  894.         if (stmt.row.seq > 1) {//excludes main & temp
  895.           var oRow = {};
  896.  
  897.           oRow.seq = stmt.row.seq;
  898.           oRow.name = stmt.row.name;
  899.           oRow.file = stmt.row.file;
  900.  
  901.           aRows.push(oRow);
  902.         }
  903.       }
  904.     } finally {
  905.       stmt.reset();
  906.     }
  907.     return aRows;
  908.   },
  909.  
  910. //function for db list (main, temp and attached)
  911. //returns only name, not file
  912.   getDatabaseList: function() {
  913.     var sQuery = "PRAGMA database_list";
  914.     var stmt = this.dbConn.createStatement(sQuery);
  915.     var aRows = ["main", "temp"];
  916.     try {
  917.       while (stmt.executeStep()) {
  918.         if (stmt.row.seq > 1) //sometimes, temp is not returned
  919.           aRows.push(stmt.row.name);
  920.       }
  921.     } finally {
  922.       stmt.reset();
  923.     }
  924.     return aRows;
  925.   },
  926.  
  927.   getForeignKeyList: function(sTableName, sDbName) {
  928.     var sQuery = this.getPragmaSchemaQuery("foreign_key_list", sTableName, sDbName);
  929.     var stmt = this.dbConn.createStatement(sQuery);
  930.     var aRows = [];
  931.     try {
  932.       while (stmt.executeStep()) {
  933.         var oRow = {};
  934.  
  935.         oRow.id = stmt.row.id;
  936.         oRow.seq = stmt.row.seq;
  937.         oRow.table = stmt.row.table;
  938.         oRow.from = stmt.row.from;
  939.         oRow.to = stmt.row.to;
  940.         oRow.on_update = stmt.row.on_update;
  941.         oRow.on_delete = stmt.row.on_delete;
  942.         oRow.match = stmt.row.match;
  943.  
  944.         aRows.push(oRow);
  945.       }  
  946.     } finally {
  947.       stmt.reset();
  948.     }
  949.     return aRows;
  950.   },
  951.  
  952.   getTableInfo: function(sTableName, sDbName) {
  953.     var sQuery = this.getPragmaSchemaQuery("table_info", sTableName, sDbName);
  954.     var stmt = this.dbConn.createStatement(sQuery);
  955.     var aRows = [];
  956.     try {
  957.       while (stmt.executeStep()) {
  958.         var oRow = {};
  959.  
  960.         oRow.cid = stmt.row.cid;
  961.         oRow.name = stmt.row.name;
  962.         oRow.type = stmt.row.type;
  963.         oRow.notnull = stmt.row.notnull;
  964.         oRow.dflt_value = stmt.row.dflt_value;
  965.         oRow.pk = stmt.row.pk;
  966.  
  967.         aRows.push(oRow);
  968.       }  
  969.     } finally {
  970.       stmt.reset();
  971.     }
  972.     return aRows;
  973.   },
  974.  
  975.   getIndexList: function(sTableName, sDbName) {
  976.     var sQuery = this.getPragmaSchemaQuery("index_list", sTableName, sDbName);
  977.     var stmt = this.dbConn.createStatement(sQuery);
  978.     var aRows = [];
  979.     try {
  980.       while (stmt.executeStep()) {
  981.         var oRow = {};
  982.  
  983.         oRow.seq = stmt.row.seq;
  984.         oRow.name = stmt.row.name;
  985.         oRow.unique = stmt.row.unique;
  986.  
  987.         aRows.push(oRow);
  988.       }  
  989.     } finally {
  990.       stmt.reset();
  991.     }
  992.     return aRows;
  993.   },
  994.  
  995.   getIndexInfo: function(sIndexName, sDbName) {
  996.     var sQuery = this.getPragmaSchemaQuery("index_info", sIndexName, sDbName);
  997.     var stmt = this.dbConn.createStatement(sQuery);
  998.     var aRows = [];
  999.     try {
  1000.       while (stmt.executeStep()) {
  1001.         var oRow = {};
  1002.  
  1003.         oRow.seqno = stmt.row.seqno;
  1004.         oRow.cid = stmt.row.cid;
  1005.         oRow.name = stmt.row.name;
  1006.  
  1007.         aRows.push(oRow);
  1008.       }  
  1009.     } finally {
  1010.       stmt.reset();
  1011.     }
  1012.     return aRows;
  1013.   },
  1014.  
  1015.   getCollationList: function(sIndexName, sDbName) {
  1016.     var sQuery = "PRAGMA collation_list";
  1017.     var stmt = this.dbConn.createStatement(sQuery);
  1018.     var aRows = [];
  1019.     try {
  1020.       while (stmt.executeStep()) {
  1021.         var oRow = {};
  1022.  
  1023.         oRow.seq = stmt.row.seq;
  1024.         oRow.name = stmt.row.name;
  1025.  
  1026.         aRows.push(oRow);
  1027.       }  
  1028.     } finally {
  1029.       stmt.reset();
  1030.     }
  1031.     return aRows;
  1032.   }
  1033. }
  1034.  
  1035. var SQLiteFn = {
  1036.   msStrForNull: 'NULL',
  1037.   msQuoteChar: '""',
  1038.  
  1039.   getStrForNull: function() { return this.msStrForNull; },
  1040.   setStrForNull: function(sStrForNull) {
  1041.     this.msStrForNull = sStrForNull.toUpperCase();
  1042.   },
  1043.  
  1044.   setQuoteChar: function(sQuoteChar) {
  1045.     this.msQuoteChar = sQuoteChar;
  1046.   },
  1047.  
  1048.   quoteIdentifier: function(str) {
  1049.   //http://sqlite.org/lang_keywords.html
  1050.   //"keyword" A keyword in double-quotes is an identifier
  1051.   //assume there is no " within the identifier's name
  1052.     return this.msQuoteChar[0] + str + this.msQuoteChar[1];
  1053.   },
  1054.  
  1055.   quote: function(str) {
  1056.     if (typeof str == "string")
  1057.       str = str.replace("'", "''", "g");
  1058.     return "'" + str + "'";
  1059.   },
  1060.  
  1061.   //convert the argument into a format suitable for use in DEFAULT clause in column definition.
  1062.   makeDefaultValue: function(str) {
  1063.     if (str.length == 0)
  1064.       return null;
  1065.     else
  1066.       return str;
  1067.   },
  1068.  
  1069.   makeSqlValue: function(str) { 
  1070.     //for all str (typeof str == "string") seems true
  1071.     //so how do we tell numbers from strings?
  1072.     if (typeof str == "string") {
  1073.       var sUp = str.toUpperCase();
  1074.       if (sUp == this.getStrForNull() || sUp.length == 0)
  1075.         return this.getStrForNull();
  1076.       if (sUp == "CURRENT_DATE" || sUp == "CURRENT_TIME" || sUp == "CURRENT_TIMESTAMP")
  1077.         return str.toUpperCase();
  1078.  
  1079.       //TODO: use regexp for patterns that should be treated as numbers.
  1080.       if(!isNaN(str)) {
  1081.         //if str can become a number, do not do so in the following 2 conditions:
  1082.         //1. if it begins with "0" but not with "0."
  1083.         if (str.indexOf('0') == 0) {
  1084.           if (str.indexOf('.') == 1)
  1085.             return Number(str);
  1086.           else
  1087.             return this.quote(str);
  1088.         }
  1089.         //2. if it has space
  1090.         if (str.indexOf(' ') != -1)
  1091.           return this.quote(str);
  1092.         //otherwise, return a number
  1093.         return Number(str);
  1094.       }
  1095.  
  1096.       return this.quote(str);
  1097.     }
  1098.     else
  1099.       return str;
  1100.   },
  1101.  
  1102.   defaultValToInsertValue: function(str) {
  1103.     if (typeof str != "string")
  1104.       return str;
  1105.     if (str.length == 0)
  1106.       return "";
  1107.     if (str.toUpperCase() == this.getStrForNull())
  1108.       return "";
  1109.     var ch = str[0];
  1110.     if (ch != "'" && ch != '"')
  1111.       return str;
  1112.  
  1113.     var newStr = "";
  1114.     for (var i = 1; i < str.length - 1; i++) {
  1115.       if (i >= 2)
  1116.         if (str[i] == ch && str[i-1] == ch)
  1117.           continue;
  1118.  
  1119.       newStr += str[i];
  1120.     }
  1121.     return newStr;
  1122.   },
  1123.  
  1124.   blob2hex: function(aData) {
  1125.     var hex_tab = '0123456789ABCDEF';
  1126.     var str = '';
  1127.     for (var i = 0; i < aData.length; i++) {
  1128.       str += hex_tab.charAt(aData[i] >> 4 & 0xF) + hex_tab.charAt(aData[i] & 0xF);
  1129.     }
  1130.     return "X'" + str + "'";
  1131.   }
  1132. };
  1133.  
  1134. //for export purposes
  1135. function getCsvFromArray(arrData, arrTypes, arrColumns, oCsv) {
  1136.   var strDelimiter = oCsv.delimiter;
  1137.   if(oCsv.bColNames) {
  1138.     var arrRow = [], types = [];
  1139.     var i = 0;
  1140.     for(var i in arrColumns) {
  1141.       arrRow.push(arrColumns[i][0]);
  1142.       types.push(SQLiteTypes.TEXT);
  1143.     }
  1144.     var data = getCsvRowFromArray(arrRow, types, oCsv);
  1145.     aLines.push(data);
  1146.   }
  1147.  
  1148.   for(var i = 0; i < arrData.length; i++) {
  1149.     var arrRow = arrData[i];
  1150.     var types = arrTypes[i];
  1151.     var data = getCsvRowFromArray(arrRow, types, oCsv);
  1152.     aLines.push(data);
  1153.   }
  1154.   return aLines.join("\n");
  1155. }
  1156.  
  1157. function getCsvRowFromArray(arrRow, arrTypes, oCsv) {
  1158.   var strDelimiter = oCsv.delimiter;
  1159.   if (arrTypes == []) {
  1160.     for (var i = 0; i < arrRow.length; i++)
  1161.       arrTypes.push(SQLiteTypes.TEXT);
  1162.   }
  1163.  
  1164.   for (var i = 0; i < arrRow.length; i++) {
  1165.     switch (arrTypes[i]) {
  1166.       case SQLiteTypes.INTEGER:
  1167.       case SQLiteTypes.FLOAT:
  1168.       case SQLiteTypes.BLOB:
  1169.         break;
  1170.       case SQLiteTypes.NULL: 
  1171.         arrRow[i] = "";
  1172.         break;
  1173.       case SQLiteTypes.TEXT:
  1174.       default:
  1175.         arrRow[i] = arrRow[i].replace("\"", "\"\"", "g");
  1176.         arrRow[i] = '"' + arrRow[i] + '"';
  1177.         break;
  1178.     }
  1179.   }
  1180.   return arrRow.join(strDelimiter);
  1181. }
  1182.  
  1183. //arrays populated on 2009-09-13
  1184. //SQLite Core Functions
  1185. //http://sqlite.org/lang_corefunc.html
  1186. var funcNamesCore = ['abs', 'changes', 'coalesce', 'glob', 'ifnull', 'hex', 'last_insert_rowid', 'length', 'like', 'load_extension', 'lower', 'ltrim', 'max', 'min', 'quote', 'random', 'replace', 'round', 'rtrim', 'soundex', 'sqlite_source_id', 'sqlite_version', 'substr', 'total_changes', 'trim', 'typeof', 'upper', 'zeroblob'];
  1187.  
  1188. //SQLite Aggregate Functions
  1189. //http://sqlite.org/lang_aggfunc.html
  1190. var funcNamesAggregate = ['avg', 'count', 'group_concat', 'max', 'min', 'sum', 'total'];
  1191.  
  1192. //SQLite Date And Time Functions
  1193. //http://sqlite.org/lang_datefunc.html
  1194. var funcNamesDate = ['date', 'time', 'datetime', 'julianday', 'strftime'];
  1195.  
  1196. var funcNamesAll = [];
  1197. funcNamesAll = funcNamesAll.concat(funcNamesCore);
  1198. funcNamesAll = funcNamesAll.concat(funcNamesAggregate);
  1199. funcNamesAll = funcNamesAll.concat(funcNamesDate);
  1200.  
  1201.